package com.example.lotapp.utils.json;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.text.TextUtils;

public class Json {
	public static final Set<Class<?>> PRIMITIVECLASS = new HashSet<Class<?>>();
	static {
		PRIMITIVECLASS.add(boolean.class);
		PRIMITIVECLASS.add(byte.class);
		PRIMITIVECLASS.add(short.class);
		PRIMITIVECLASS.add(int.class);
		PRIMITIVECLASS.add(long.class);
		PRIMITIVECLASS.add(float.class);
		PRIMITIVECLASS.add(double.class);

		PRIMITIVECLASS.add(Boolean.class);
		PRIMITIVECLASS.add(Byte.class);
		PRIMITIVECLASS.add(Short.class);
		PRIMITIVECLASS.add(Integer.class);
		PRIMITIVECLASS.add(Long.class);
		PRIMITIVECLASS.add(Float.class);
		PRIMITIVECLASS.add(Double.class);

		PRIMITIVECLASS.add(BigInteger.class);
		PRIMITIVECLASS.add(BigDecimal.class);
		PRIMITIVECLASS.add(String.class);
		PRIMITIVECLASS.add(Date.class);
	}
	
	public static <T> T parseObject(String json, Class<T> classz) {
		if (TextUtils.isEmpty(json) || classz == null) {
			return null;
		}
		try {
			JSONObject object = new JSONObject(json);
			return parseObject(object, classz);
		} catch (JSONException e) {
		}
		return null;
	}
	
	public static <T> T parseObject(JSONObject jsonObj, Class<T> classz) {
		if (jsonObj == null) {
			return null;
		}
		try {
			T obj = classz.newInstance();
			for (Field field : classz.getDeclaredFields()) {
				if (field == null) {
					continue;
				}
				JsonField jsonField = field.getAnnotation(JsonField.class);
				if (jsonField == null || TextUtils.isEmpty(jsonField.name())) {
					continue;
				}
				Object value = jsonObj.opt(jsonField.name());
				if (value == null) {
					continue;
				}
				
				if (isPrimitiveType(field.getType())) {
					setFieldValue(field, jsonField, obj, value);
				} else {
					Class<?> childClasss = field.getType();
					Object child = null;
					if (value instanceof JSONArray) {
						ParameterizedType pt = (ParameterizedType)field.getGenericType();
						if (pt != null && pt.getActualTypeArguments().length > 0) {
							child = parseList((JSONArray)value, (Class)pt.getActualTypeArguments()[0]);
						}
					} else {
						child = parseObject(value.toString(), childClasss);
					}
					if (child != null) {
						field.set(obj, child);
					}
				}
			}
			return obj;
		} catch (InstantiationException e) {
		} catch (IllegalAccessException e) {
		}
		
		return null;
	}
	
	public static <T> Collection<T> parseList(String json, Class<T> classz) {
		if (TextUtils.isEmpty(json) || classz == null) {
			return null;
		}
		try {
			JSONArray array = new JSONArray(json);
			return parseList(array, classz);
		} catch (JSONException e) {
		}
		return null;
	}
	
	public static <T> Collection<T> parseList(JSONArray array, Class<T> classz) {
		if (array == null) {
			return null;
		}
		List<T> list = new ArrayList<T>();
		int len = array.length();
		for (int i  = 0; i < len; ++i) {
			JSONObject object = array.optJSONObject(i);
			if (object == null) {
				continue;
			}
			T tmp = parseObject(object, classz);
			if (tmp != null) {
				list.add(tmp);
			}
		}
		return list;
	}
	
	public static boolean isPrimitiveType(Class<?> classz) {
		return PRIMITIVECLASS.contains(classz);
	}
	
	public static <T> void setFieldValue(Field field, JsonField jsonField, T object, Object value) throws IllegalAccessException{
		final Class<?> type = field.getType();
		final String name = type.getName();
		if (type == Boolean.class || "boolean".equals(name)) {
			Boolean v = JsonType.toBoolean(value);
			if (v != null) {
				field.setBoolean(object, v);
			}
		} else if (type == Byte.class || "byte".equals(name)) {
			Byte v = JsonType.toByte(value);
			if (v != null) {
				field.setByte(object, v);
			}
		} else if (type == Short.class || "short".equals(name)) {
			Short v = JsonType.toShort(value);
			if (v != null) {
				field.setShort(object, v);
			}
		} else if (type == Integer.class || "int".equals(name)) {
			Integer v = JsonType.toInteger(value);
			if (v != null) {
				field.setInt(object, v);
			}
		} else if (type == Long.class || "long".equals(name)) {
			Long v = JsonType.toLong(value);
			if (v != null) {
				field.setLong(object, v);
			}
		} else if (type == Float.class || "float".equals(name)) {
			Float v = JsonType.toFloat(value);
			if (v != null) {
				field.setFloat(object, v);
			}
		} else if (type == Double.class || "double".equals(name)) {
			Double v = JsonType.toDouble(value);
			if (v != null) {
				field.setDouble(object, v);
			}
		} else if (type == String.class) {
			if (value instanceof String) {
				field.set(object, value);
			}
		} else if (type == BigInteger.class){
			BigInteger v = JsonType.toBigInteger(value);
			if (v != null) {
				field.set(object, v);
			}
		} else if (type == BigDecimal.class){
			BigDecimal v = JsonType.toBigDecimal(value);
			if (v != null) {
				field.set(object, v);
			}
		} else if (type == Date.class){
			Date v = JsonType.toDate(value, jsonField.format());
			if (v != null) {
				field.set(object, v);
			}
		}
	}
}
